﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Swift;
using Utils;
using TB;

namespace Server {

    public static class ActorExtention {

        public static Timer Timer;
        public static CoroutineManager CM;

        public static void EatFood(this Actor a, Actor food, ActorFactory factory) {

            switch(food.Type) {
                case 1: {  // 升级
                        if(a.Blood < 3) {
                            a.Blood++;
                            factory.Refresh (a);
                        }
                    }
                    break;
                case 2: {  // 暂停
                        factory.PauseAllEnemy ();
                    }
                    break;
                case 3: {  // 炸弹
                        factory.KillAllByTag ("enemy");
                    }
                    break;
                case 4: {  // 无敌
                        a.CanKill = false;
                        //TimerUtils.WaitForMsec (food.T * 1000, () => {
                        //    a.CanKill = true;
                        //});
                        int i = food.T;
                        Timer.AddEvent ("CanKill" + a.GetHashCode (), 0, 1000, (name) => {
                            if(a.Alive == false) {
                                Timer.RemoveEvent (name);
                                return;
                            }
                            i--;
                            if(i < 0) {
                                a.CanKill = true;
                                Timer.RemoveEvent (name);
                            }
                        });
                    }
                    break;
                case 5: {  // 铁锹

                        factory.KillAllByTag ("basewall");

                        for(int i = 23; i <= 25; i++) {
                            for(int j = 11; j <= 14; j++) {
                                if(i >= 24 && j >= 12 && j <= 13)
                                    continue;
                                factory.CreateActor (2, i, j, 0.5f, "basewall");
                            }
                        }

                        Timer.RemoveEvent ("food5_" + factory.GetHashCode ());

                        int t = food.T;
                        Timer.AddEvent ("food5_" + factory.GetHashCode (), 0, 1000, (name) => {
                            if(factory.Alive == false) {
                                Timer.RemoveEvent (name);
                                return;
                            }
                            t--;
                            if(t < 0) {
                                factory.KillAllByTag ("basewall");

                                for(int i = 23; i <= 25; i++) {
                                    for(int j = 11; j <= 14; j++) {
                                        if(i >= 24 && j >= 12 && j <= 13)
                                            continue;
                                        factory.CreateWoodWall (1001, i, j);
                                    }
                                }

                                Timer.RemoveEvent (name);
                            }
                        });
                    }
                    break;
                case 6: {  // 加生命
                        a.Life++;
                    }
                    break;
                default:
                    throw new Exception ("找不到 食物类型 = " + food.Type);
            }
            factory.RemoveActor (food);
        }

        public static void Move(this Actor a, int dir, ActorFactory factory) {
            factory.Add( CM.Start (a.Mov (dir, factory)));
        }

        public static void Attack(this Actor a, ActorFactory factory, Action<string> callback = null) {

            if(a.Cooling == true) {
                a.Cooling = false;

                Actor bullet = factory.CreateActor (10, a.AX, a.AY, 0.25f, "bullet-" + a.Tag, a.Dir);
                bullet.Owner = a;
                bullet.ATK = a.ATK;
                bullet.Speed = a.BulletSpeed;
                factory.NeedLocomotor (bullet);

                factory.Add(CM.Start (bullet.GoAhead (factory, (obstacles) => {

                    bool flag = false;
                    for(int i = 0; i < obstacles.Count; i++) {
                        if(obstacles[i].CanKill == false) {
                            factory.RemoveActor (bullet);
                            continue;
                        }
                        if(obstacles[i].Tag == "enemy") {               // 如果是敌人
                            if(a.Tag == "enemy") {
                                continue;
                            } else {
                                if(obstacles[i].WithFood == true) {     // 如果携带着食物 
                                    obstacles[i].WithFood = false;
                                    if(callback != null) {
                                        callback ("food");
                                    }
                                } else {
                                    obstacles[i].Blood -= bullet.ATK;
                                    if(obstacles[i].Blood <= 0) {
                                        callback ("enemy" + (obstacles[i].TID / 100 - 3));
                                        factory.RemoveActor (obstacles[i]);
                                    } else {
                                        factory.Refresh (obstacles[i]);
                                    }
                                }
                            }
                        } else if(obstacles[i].Tag == "player1") {
                            if(a.Tag == "player1") {
                                continue;
                            } else if(a.Tag == "player2") {

                                Timer.RemoveEvent ("punish" + obstacles[i].GetHashCode ());
                                obstacles[i].Paused = true;
                                Actor tmp = obstacles[i];
                                int t = a.ATK * 5;
                                Timer.AddEvent ("punish" + obstacles[i].GetHashCode (), 0, 1000, (name) => {
                                    if(tmp.Alive == false) {
                                        Timer.RemoveEvent (name);
                                        return;
                                    }
                                    t--;
                                    if(t < 0) {
                                        tmp.Paused = false;
                                        Timer.RemoveEvent (name);
                                    }
                                });
                            } else {
                                obstacles[i].Blood -= bullet.ATK;
                                if(obstacles[i].Blood <= 0) {
                                    factory.RemoveActor (obstacles[i]);

                                    if(callback != null) {
                                        callback ("player1");
                                    }

                                } else {
                                    factory.Refresh (obstacles[i]);
                                }
                            }
                        } else if(obstacles[i].Tag == "player2") {
                            if(a.Tag == "player2") {
                                continue;
                            } else if(a.Tag == "player1") {

                                Timer.RemoveEvent ("punish" + obstacles[i].GetHashCode ());
                                obstacles[i].Paused = true;
                                Actor tmp = obstacles[i];
                                int t = a.ATK * 5;
                                Timer.AddEvent ("punish" + obstacles[i].GetHashCode (), 0, 1000, (name) => {
                                    if(tmp.Alive == false) {
                                        Timer.RemoveEvent (name);
                                        return;
                                    }
                                    t--;
                                    if(t < 0) {
                                        tmp.Paused = false;
                                        Timer.RemoveEvent (name);
                                    }
                                });
                            } else {
                                obstacles[i].Blood -= bullet.ATK;
                                if(obstacles[i].Blood <= 0) {
                                    factory.RemoveActor (obstacles[i]);

                                    if(callback != null) {
                                        callback ("player2");
                                    }

                                } else {
                                    factory.Refresh (obstacles[i]);
                                }
                            }
                        } else {
                            if(obstacles[i].TID == 2) {  // 铁墙 
                                if(a.ATK == 1) {
                                    factory.RemoveActor (bullet);
                                    continue;
                                }
                            }

                            factory.RemoveActor (obstacles[i].GetImplicativeActor (bullet.Dir));
                            factory.RemoveActor (obstacles[i]);
                        }
                        flag = true;
                    }


                    if(flag == true || obstacles.Count == 0) {
                        factory.RemoveActor (bullet);
                    }

                })));


            }

        }

        /// <summary>
        /// 直走
        /// </summary>
        /// <param name="a"></param>
        /// <param name="callback"> 遇到障碍物，就回调 </param>
        /// <returns></returns>
        public static IEnumerator GoAhead(this Actor a, ActorFactory factory, Action<List<Actor>> callback = null) {
            List<Actor> obstacles;
            while(true) {
                yield return null;

                a.Move (0.5f / a.Speed);

                if(factory.IsBlock (a, out obstacles)) {
                    if(callback != null) {
                        callback (obstacles);
                    }
                }

                if(a.Alive == false) {
                    break;
                }
            }

            yield return null;
        }

        public static IEnumerator Mov(this Actor a, int dir, ActorFactory factory) {

            if(a.Paused == false && a.Moving == false) {
                a.Moving = true;

                int k = a.Slide == false ? 1 : 3;
                a.Dir = (DIR) dir;
                for(int i = 0; a.Alive == true && i < a.Speed; i++) {
                    a.Move (0.5f * k / a.Speed);

                    a.Slide = false;
                    List<Actor> foods = null;
                    if(
                        factory.IsBlock (a, (o) => {
                            if(o.Tag == "slide") {          // 滑地  
                                a.Slide = true;
                                return false;
                            } else if(o.Tag == "food") {    // 食物
                                if(foods == null) {
                                    foods = new List<Actor> ();
                                }
                                foods.Add (o);
                                return false;
                            }
                            return true;
                        })
                    ) {
                        break;
                    }

                    if(foods != null) {
                        for(int u = 0; u < foods.Count; u++) {
                            a.EatFood (foods[u], factory);
                        }
                    }

                    yield return null;
                }

                a.Moving = false;
            }

            yield return null;
        }

        public static IEnumerator Stupid(this Actor a, ActorFactory factory, Action<string> callback) {

            while(a.Alive) {

                if(a.Paused == false) {

                    a.Move (0.5f / a.Speed);

                    if(
                        factory.IsBlock (a, (o) => {
                            if(o.Tag == "slide" || o.Tag == "food") {
                                return false;
                            }
                            return true;
                        })
                    ) {
                        a.Dir = (DIR) RandomUtils.Random (0, 4);
                    }

                    if(RandomUtils.Random (0, 100) > 90) {
                        a.Attack (factory, callback);
                    }

                }
                yield return null;
            }

            yield return null;
        }

        public static void StartAI(this Actor a, ActorFactory factory, Action<string> callback) {

            switch(a.AI) {
                default:
                    factory.Add(CM.Start (Stupid (a, factory, callback)));
                    break;
            }

        }

    }
}
